import json
from os.path import join

from app.db.search import es
from app.model import mysql
from app.service.media import get_media, get_media_custom_tags
from app.settings.config import ROOT_DIR


async def init_all_mapping():
    """
    初始化索引
    :return:
    """
    mapping_path = join(ROOT_DIR, 'app/settings/es.mapping.json')
    with open(mapping_path, 'r') as f:
        mappings = json.loads(f.read())
    for schema in mappings:
        index = schema.get('index', '')
        body = schema.get('body', {})
        try:
            has_index = await es.indices.exists(index)
            if not has_index:
                await es.indices.create(index)
            await es.indices.put_mapping(body, index=index)
        except Exception as e:
            print(e)


async def index_my_media(user_id, media_id):
    """
    索引我的媒体
    :param user_id:
    :param media_id:
    :return:
    """
    index_name = 'designer_my_media'
    index_id = f'{user_id}_{media_id}'
    my_media = await mysql.MyMedia.get_or_none(user_id=user_id, media_id=media_id)
    if not my_media:
        # 删除索引
        try:
            await es.delete(index_name, index_id)
        except Exception as e:
            pass
        return
    # ---- 添加索引 ----
    media = await get_media(media_id)
    if not media or not media.data:
        # 删除索引
        try:
            es.delete(index_name, index_id)
        except Exception as e:
            print(e)
        return
    title = ''
    m_type = 1
    system_tags = json.loads(media.data.system_tags) if media.data.system_tags else []
    custom_tags = await get_media_custom_tags(user_id, media_id)
    if media.object_type == mysql.Media.Type.photo:
        title = media.data.text or ''
        m_type = 1
    elif media.object_type == mysql.Media.Type.case:
        title = media.data.title or ''
        if media.data.kind == 1:
            m_type = 2
        elif media.data.kind == 2:
            m_type = 3
    elif media.object_type == mysql.Media.Type.video:
        title = media.data.text or ''
        m_type = 4
    elif media.object_type == mysql.Media.Type.panorama:
        title = media.data.text or ''
        m_type = 5
    folder_ids = await mysql.FolderFile.filter(user_id=user_id, media_id=media_id).values_list('folder_id', flat=True)
    all_folder_ids_set = set([])
    for folder_id in folder_ids:
        folder = await mysql.Folder.get_or_none(id=folder_id)
        if not folder:
            continue
        all_folder_ids_set.add(folder.id)
        parent_path = folder.parent_path or ''
        parent_ids = parent_path.strip('/') if parent_path else ''
        folder_parent_ids = [int(i) for i in parent_ids.split('/')] if parent_ids else []
        for folder_parent_id in folder_parent_ids:
            all_folder_ids_set.add(folder_parent_id)
    all_folder_ids = list(all_folder_ids_set)
    # 时间归类
    year = my_media.created_time.year,
    year_month = int(f'{my_media.created_time.year}{my_media.created_time.month}'),
    year_month_day = int(f'{my_media.created_time.year}{my_media.created_time.month}{my_media.created_time.day}'),
    doc = {
        "id": my_media.id,
        "user_id": user_id,
        "media_id": media_id,
        "title": title,
        "folder_ids": all_folder_ids,
        "custom_tags": custom_tags,
        "system_tags": system_tags,
        "type": m_type,
        "whole_case": media.is_house_case,
        "year": year,
        "month": year_month,
        "day": year_month_day,
        "created_time": my_media.created_time.strftime("%Y-%m-%d %H:%M:%S"),
    }
    try:
        await es.index(index_name, doc, id=index_id)
    except Exception as e:
        print(e)


async def search_my_media(params: dict):
    """
    搜索我的媒体
    :param params:
        keyword: 关键词
        offset： 游标
        limit： 翻页数
        time_type： 时间类型，全部-0、年-year、月-month、日-day
        time_value： 时间的具体数值，例如：2020、202007、20200712
        media_type： 媒体类型，全部-0、全屋案例-1、3D全景-2、图片-3
    :return:
    """
    keyword = params.get('keyword', '')
    offset = params.get('offset', 0)
    limit = params.get('limit', 10)
    user_id = params.get('user_id', 0)
    time_type = params.get('time_type', 0)
    time_value = params.get('time_value', 0)
    media_type = params.get('media_type', 0)
    folder_ids = params.get('folder_ids', [])

    index_name = 'designer_my_media'
    search_body = {
        'query': {
            'bool': {}
        },
        'from': offset,
        'size': limit,
        'sort': [
            {'created_time': {'order': 'desc'}}
        ],
        'aggs': {},
        "_source": ["type", "user_id", "media_id"],
    }
    must_filters = []
    should_filters = []
    must_not_filters = []

    is_agg = False

    if keyword or media_type or (time_type and time_value) or user_id or folder_ids:
        if user_id:
            must_filters.append({"term": {"user_id": {"value": user_id}}})
        if keyword:
            should_filters.append({"match": {"title": {"query": keyword, "boost": 3}}})
            should_filters.append({"term": {"custom_tags": {"value": keyword, "boost": 2}}})
            should_filters.append({"term": {"system_tags": {"value": keyword, "boost": 1}}})
        if media_type:
            must_filters.append({"term": {"type": {"value": media_type}}})
        if folder_ids:
            must_filters.append({"terms": {"folder_ids": folder_ids}})
        if time_type and time_value:
            must_filters.append({"term": {f"{time_type}": {"value": time_value}}})
    if keyword:  # 关键词搜索按照boost权重排序
        search_body["sort"] = []
    if time_type and not time_value:  # 按时间分组
        is_agg = True
        search_body["from"] = 0
        search_body["size"] = 0
        search_body["aggs"] = {
            f"time_type": {
                "terms": {"field": time_type},
                "aggs": {"first": {"top_hits": {"size": 1, "_source": ""}}}
            }
        }
        pass
    if should_filters:
        must_filters.append({"bool": {"should": should_filters}})
    if must_filters:
        search_body['query']['bool'].update({'must': must_filters})
    if must_not_filters:
        search_body['query']['bool'].update({'must_not': must_not_filters})

    # print(json.dumps(search_body, ensure_ascii=False, indent=True))
    search_result = await es.search(body=search_body, index=index_name)

    # 数据
    items = []
    if is_agg:
        agg_buckets = search_result.get('aggregations', {}).get('time_type', {}).get('buckets', [])
        # print(agg_buckets)
        need_agg_buckets = agg_buckets[offset: (offset + limit)]
        # print(json.dumps(need_agg_buckets, ensure_ascii=False, indent=True))
        for need_agg_bucket in need_agg_buckets:
            key = need_agg_bucket.get('key', '')
            first = need_agg_bucket.get('first', {})
            hits = first.get('hits', {}).get('hits', [])
            hit = hits[0] if hits else None
            hit_id = hit.get('_id', '') if hit else ''
            hit_id_info = list(map(int, hit_id.split('_'))) if hit_id else []
            m_user_id = hit_id_info[0] if len(hit_id_info) > 0 else 0
            media_id = hit_id_info[1] if len(hit_id_info) > 1 else 0
            items.append({
                "type": 6,
                "desc": key
            })
            items.append({
                "type": 7,
                "user_id": m_user_id,
                "media_id": media_id,
                "time": key,
            })
        total = len(need_agg_buckets)
        # print(json.dumps(items, ensure_ascii=False, indent=True))
    else:
        # print(json.dumps(search_result, ensure_ascii=False, indent=True))
        total = search_result.get('hits', {}).get('total', {}).get('value', 0)
        hits = search_result.get('hits', {}).get('hits', [])
        for hit in hits:
            _source = hit.get('_source', {})
            media_type = _source.get('type', 0)
            m_user_id = _source.get('user_id', 0)
            media_id = _source.get('media_id', 0)
            items.append({
                "type": media_type,
                "user_id": m_user_id,
                "media_id": media_id,
            })
        # print(json.dumps(items, ensure_ascii=False, indent=True))
    return is_agg, total, items


async def index_home_media(media_id):
    """
    索引首页模块推荐
    :param media_id:
    :return:
    """
    index_name = 'designer_home'
    index_id = media_id

    media = await get_media(media_id)
    if not media or not media.data:
        # 删除索引
        try:
            es.delete(index_name, index_id)
        except Exception as e:
            print(e)
        return
    title = ''
    m_type = 1
    system_tags = json.loads(media.data.system_tags) if media.data.system_tags else []
    custom_tags = await get_media_custom_tags(media.user_id, media_id)
    recommended_tags = await get_media_custom_tags(-1, media_id)
    if media.object_type == mysql.Media.Type.photo:
        title = media.data.text or ''
        m_type = 1
    elif media.object_type == mysql.Media.Type.case:
        title = media.data.title or ''
        if media.data.kind == 1:
            m_type = 2
        elif media.data.kind == 2:
            m_type = 3
    elif media.object_type == mysql.Media.Type.video:
        title = media.data.text or ''
        m_type = 4
    elif media.object_type == mysql.Media.Type.panorama:
        title = media.data.text or ''
        m_type = 5
    tags = []
    tags.extend(custom_tags)
    tags.extend(system_tags)
    tags.extend(recommended_tags)
    doc = {
        "id": media.id,
        "title": title,
        "custom_tags": custom_tags,
        "system_tags": system_tags,
        "recommended_tags": recommended_tags,
        "tags": tags,
        "type": m_type,
        "whole_case": media.is_house_case,
        "created_time": media.created_time.strftime("%Y-%m-%d %H:%M:%S"),
    }
    try:
        await es.index(index_name, doc, id=index_id)
    except Exception as e:
        print(e)


async def index_master(master_id):
    """
    索引大师
    :param master_id:
    :return:
    """
    index_name = 'designer_master'
    index_id = f'{master_id}'
    master = await mysql.User.get_or_none(id=master_id)
    master_info = await mysql.UserInfo.get_or_none(id=master_id)

    if not master:
        # 删除索引
        try:
            await es.delete(index_name, index_id)
        except Exception as e:
            pass
        return
    doc = {
        "id": master.id,
        "nickname": master.nickname,
        "style": master_info.style,
        "text": master_info.personal_profile,
        "house_type": master_info.house_type,
        "country": master_info.country
    }
    try:
        await es.index(index_name, doc, id=index_id)
    except Exception as e:
        print(e)


async def designer_master_case(case_id):
    """
    索引大师
    :param case_id:
    :return:
    """
    index_name = 'designer_master_case'
    index_id = f'{case_id}'
    case = await mysql.Media.get_or_none(id=case_id)
    user = await mysql.User.get_or_none(id=case.user_id)
    userinfo = await mysql.UserInfo.get_or_none(id=case.user_id)
    text = ""
    system_tags = ""
    if not case:
        # 删除索引
        try:
            await es.delete(index_name, index_id)
        except Exception as e:
            pass
        return
    if case.object_type == mysql.Media.Type.photo:
        photo = await mysql.Photo.get_or_none(id=case.object_id)
        text = photo.text
        system_tags = photo.system_tags
    elif case.object_type == mysql.Media.Type.case:
        media = await mysql.Case.get_or_none(id=case.object_id)
        text = media.text
        system_tags = media.system_tags
    elif case.object_type == mysql.Media.Type.video:
        video = await mysql.Video.get_or_none(id=case.object_id)
        text = video.text
        system_tags = video.system_tags

    doc = {
        "id": case.id,
        "nickname": user.nickname,
        "text": text,
        "system_tags": system_tags,
        "country": userinfo.country,
        "user_id": user.id,
    }
    try:
        await es.index(index_name, doc, id=index_id)
    except Exception as e:
        print(e)


async def search_master(params: dict):
    """
    搜索我的媒体
    :param params:
        keyword: 关键词
        offset： 游标
        limit： 翻页数
        navs:
    :return:
    """
    keyword = params.get('keyword', '')
    offset = params.get('offset', 0)
    limit = params.get('limit', 10)
    navs = params.get('navs', 0)

    index_name = 'designer_master'
    search_body = {
        "query": {
            "multi_match": {},
        },
        "from": offset,
        "size": limit
    }
    if keyword or navs:
        if keyword:
            search_body["query"]["multi_match"].update({"query": keyword, "fields": ["nickname", "country", "text"]})
        if navs:
            search_body["query"]["multi_match"].update({"query": navs, "fields": ["style", "house_type"]})
        search_result = await es.search(body=search_body, index=index_name)
        try:
            response_list = []
            hits = search_result["hits"]["hits"]
            for item in hits:
                result = item["_source"]
                response_list.append(result)
        except:
            response_list = []
    else:
        response_list = [item.__dict__ for item in await mysql.UserInfo.filter(is_master=True).limit(limit).offset(offset)]
    return response_list


async def search_master_case(params: dict):
    """
    搜索我的媒体
    :param params:
        keyword: 关键词
        offset： 游标
        limit： 翻页数
        navs:
    :return:
    """
    keyword = params.get('keyword', '')
    offset = params.get('offset', 0)
    limit = params.get('limit', 10)
    navs = params.get('navs', 0)

    index_name = 'designer_master_case'
    search_body = {
        "query": {
            "multi_match": {},
        },
        "from": offset,
        "size": limit
    }
    if keyword or navs:
        if keyword:
            search_body["query"]["multi_match"].update({"query": keyword, "fields": ["nickname", "country", "text",
                                                                                     "system_tags"]})
        if navs:
            search_body["query"]["multi_match"].update({"query": navs, "fields": ["system_tags"]})
        search_result = await es.search(body=search_body, index=index_name)
        try:
            response_list = []
            hits = search_result["hits"]["hits"]
            for item in hits:
                result = item["_source"]
                response_list.append(result)
        except:
            response_list = []
    else:
        designer = await mysql.UserInfo.filter(is_master=True).all().values_list("id", flat=True)
        response_list = [item.__dict__ for item in await mysql.Media.filter(user_id__in=designer, parent_id=0).limit(limit).offset(offset)]
    return response_list


async def search_home_media(params: dict):
    """

    :param params:
    :return:
    """
    keyword = params.get('keyword', '')
    must_tags = params.get('must_tags', [])
    should_tags = params.get('should_tags', [])
    offset = params.get('offset', 0)
    limit = params.get('limit', 10)
    media_type = params.get('media_type', 0)
    whole_case = params.get('whole_case', None)

    index_name = 'designer_home'
    search_body = {
        'query': {
            'bool': {}
        },
        'from': offset,
        'size': limit,
        'sort': [
            {'created_time': {'order': 'desc'}}
        ],
        'aggs': {},
        "_source": {},
    }
    must_filters = []
    should_filters = []
    must_not_filters = []

    if keyword or media_type or whole_case or must_tags or should_tags:
        if keyword:
            should_filters.append({"match": {"title": {"query": keyword, "boost": 3}}})
            should_filters.append({"term": {"recommended_tags": {"value": keyword, "boost": 4}}})
            should_filters.append({"term": {"custom_tags": {"value": keyword, "boost": 2}}})
            should_filters.append({"term": {"system_tags": {"value": keyword, "boost": 1}}})
        if should_tags:
            should_filters.append({"terms": {"recommended_tags": should_tags, "boost": 4}})
            should_filters.append({"terms": {"custom_tags": should_tags, "boost": 2}})
            should_filters.append({"terms": {"system_tags": should_tags, "boost": 1}})
        if media_type:
            must_filters.append({"term": {"type": {"value": media_type}}})
        if whole_case:
            must_filters.append({"term": {"whole_case": {"value": whole_case}}})
        if must_tags:
            for must_tag in must_tags:
                must_filters.append({"term": {"tags": {"value": must_tag}}})
    if keyword: # 关键词搜索按照boost权重排序
        search_body["sort"] = []

    if should_filters:
        must_filters.append({"bool": {"should": should_filters }})
    if must_filters:
        search_body['query']['bool'].update({'must': must_filters})
    if must_not_filters:
        search_body['query']['bool'].update({'must_not': must_not_filters})

    search_result = await es.search(body=search_body, index=index_name)
    total = search_result.get('hits', {}).get('total', {}).get('value', 0)
    hits = search_result.get('hits', {}).get('hits', [])
    items = []
    for hit in hits:
        _source = hit.get('_source', {})
        media_type = _source.get('type', 0)
        media_id = _source.get('id', 0)
        items.append({
            "type": media_type,
            "media_id": media_id,
        })
        # items.append(_source)
    # print(total)
    # print(json.dumps(items, ensure_ascii=False, indent=True))
    return total, items
